﻿
namespace IssueVision.Data.Web
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel.DomainServices.Server;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server.ApplicationServices;
    using System.Web;
    using System.Web.Security;

    [EnableClientAccess]
    public class AuthenticationService : LinqToEntitiesDomainService<IssueVisionEntities>, IAuthentication<LoginUser>
    {
        #region "Private Data"
        private static readonly LoginUser DefaultUser = new LoginUser
        {
            Name = String.Empty,
            Password = String.Empty,
            Roles = new List<string>()
        };
        #endregion "Private Data"

        #region "IAuthentication<LoginUser> Interface implementation"
        /// <summary>
        /// Get the user information for the currently login user
        /// </summary>
        /// <returns></returns>
        [Query(IsComposable = false)]
        public LoginUser GetUser()
        {
            if ((ServiceContext != null) &&
                (ServiceContext.User != null) &&
                ServiceContext.User.Identity.IsAuthenticated)
            {
                return GetUserByName(ServiceContext.User.Identity.Name);
            }
            return DefaultUser;
        }

        /// <summary>
        /// Not implemented.
        /// </summary>
        /// <param name="user"></param>
        [Update]
        public void UpdateUser(LoginUser user)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Validate and login
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="isPersistent"></param>
        /// <param name="customData"></param>
        /// <returns></returns>
        public LoginUser Login(string userName, string password, bool isPersistent, string customData)
        {
            try
            {
                string userData;

                if (ValidateUser(userName, password, out userData))
                {
                    // if IsPersistent is true, will keep logged in for up to a week (or until you logout)
                    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                        /* version */ 1,
                        userName,
                        DateTime.Now, DateTime.Now.AddDays(7),
                        isPersistent,
                        userData,
                        FormsAuthentication.FormsCookiePath);

                    string encryptedTicket = FormsAuthentication.Encrypt(ticket);
                    HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);

                    if (ticket.IsPersistent)
                    {
                        authCookie.Expires = ticket.Expiration;
                    }

                    HttpContextBase httpContext = (HttpContextBase)ServiceContext.GetService(typeof(HttpContextBase));
                    httpContext.Response.Cookies.Add(authCookie);

                    return GetUserByName(userName);
                }
                return DefaultUser;
            }
            catch (Exception ex)
            {
                Exception actualException = ex;
                while (actualException.InnerException != null)
                {
                    actualException = actualException.InnerException;
                }
                throw actualException;
            }
        }

        /// <summary>
        /// Logout
        /// </summary>
        /// <returns></returns>
        public LoginUser Logout()
        {
            FormsAuthentication.SignOut();
            return DefaultUser;
        }
        #endregion "IAuthentication<LoginUser> Interface implementation"

        #region "Private Methods"
        /// <summary>
        /// Get user by user name
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        private LoginUser GetUserByName(string userName)
        {
            User foundUser = ObjectContext.Users.FirstOrDefault(u => u.Name == userName);

            if (foundUser != null)
            {
                return new LoginUser
                {
                    Name = foundUser.Name,
                    PasswordHash = foundUser.PasswordHash,
                    PasswordSalt = foundUser.PasswordSalt,
                    UserType = foundUser.UserType,
                    ProfileReset = foundUser.ProfileReset
                };
            }
            return null;
        }

        /// <summary>
        /// Validate user with password
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="userData"></param>
        /// <returns></returns>
        private bool ValidateUser(string username, string password, out string userData)
        {
            userData = null;

            LoginUser foundUser = GetUserByName(username);

            if (foundUser != null)
            {
                // generate password hash
                string passwordHash = HashHelper.ComputeSaltedHash(password, foundUser.PasswordSalt);

                if (string.Equals(passwordHash, foundUser.PasswordHash, StringComparison.Ordinal))
                {
                    userData = foundUser.UserType;
                    return true;
                }
                return false;
            }
            return false;
        }
        #endregion "Private Methods"
    }
}
